home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-13 / dte5_1.zip / HWHPXL.C < prev    next >
C/C++ Source or Header  |  1991-02-06  |  50KB  |  1,589 lines

  1. /*
  2.  * Written by Douglas Thomson (1989/1990)
  3.  *
  4.  * This source code is released into the public domain.
  5.  */
  6.  
  7. /*
  8.  * Name:    dte - Doug's Text Editor program - hardware dependent module
  9.  * Purpose: This file contains all the code that needs to be different on
  10.  *           different hardware.
  11.  * File:    hwhpxl.c (actually HWHPXLC due to naming restrictions)
  12.  * Author:  Douglas Thomson
  13.  * System:  This particular version is for the HP3000 running MPE/XL.
  14.  * Date:    October 10, 1989
  15.  * Notes:   This module has been kept as small as possible, to facilitate
  16.  *           porting between different systems.
  17.  *          This is a preliminary version, which does not support any
  18.  *           way to find out whether a character has been typed without
  19.  *           waiting for it. There seems to be no simple way to do this.
  20.  *           I tried using NOWAIT I/O on $STDIN, but it seems that a
  21.  *           pending read (even a NOWAIT read) prevents any further output
  22.  *           to the terminal!!!
  23.  *          Because of this, it is very important to use what HP call a
  24.  *           type ahead engine. This is supplied in a file called:
  25.  *              TYPE.DTS0000.TELESUP
  26.  *           and the required command is:
  27.  *              TYPE.DTS0000.TELESUP ON
  28.  *           which enables type ahead for all subsequent programs. HP
  29.  *           have some justification for not making this the default,
  30.  *           but I confess I could not follow their logic.
  31.  *          Without type ahead, "dte" loses keystrokes all over the place!
  32.  */
  33. #include "commonh"     /* dte types */
  34. #include "hwdeph"      /* prototypes for functions here */
  35. #include "utilsh"      /* for displaying messages etc */
  36. #include "versionh"    /* current version number */
  37. #include <mpe.h>       /* access to MPE intrinsics */
  38. #include <varargs.h>   /* for passing variable numbers of parameters */
  39. #include <fcntl.h>     /* for open flags */
  40.  
  41. /*
  42.  * prototypes for all functions in this file
  43.  */
  44. static void myputchar ARGS((char c));
  45. static void myputs ARGS((char *s));
  46. void error ARGS((int kind, ...));
  47. static void termset ARGS((char *name));
  48. void main ARGS((int argc, char *argv[]));
  49. static void hw_attr ARGS((char attr));
  50. static void att_stuff ARGS((void));
  51. void att_check ARGS((void));
  52. void hw_xygoto ARGS((void));
  53. int hw_clreol ARGS((void));
  54. int hw_linedel ARGS((int line));
  55. int hw_scroll_up ARGS((int top, int bottom));
  56. int hw_lineins ARGS((int line));
  57. int hw_scroll_down ARGS((int top, int bottom));
  58. int hw_c_avail ARGS((void));
  59. int hw_c_input ARGS((void));
  60. void hw_c_output ARGS((int c));
  61. void hw_terminate ARGS((void));
  62. void hw_initialize ARGS((void));
  63. void hw_move ARGS((text_ptr dest, text_ptr source, long number));
  64. int hw_backspace ARGS((void));
  65. int hw_c_insert ARGS((void));
  66. int hw_c_delete ARGS((void));
  67. int hw_rename ARGS((char *old, char *new));
  68. int hw_fattrib ARGS((char *name));
  69. int hw_set_fattrib ARGS((char *name, int attrib));
  70. int hw_unlink ARGS((char *name));
  71. int hw_printable ARGS((int c));
  72. int hw_load ARGS((char *name, text_ptr start, text_ptr limit, text_ptr *end));
  73. static int write_file ARGS((char *name, char *mode, text_ptr start,
  74.         text_ptr end));
  75. int hw_save ARGS((char *name, text_ptr start, text_ptr end));
  76. int hw_append ARGS((char *name, text_ptr start, text_ptr end));
  77. int hw_print ARGS((text_ptr start, text_ptr end));
  78. void hw_copy_path ARGS((char *old, char *name, char *new));
  79.  
  80. /*
  81.  * These pragmas provide access to system intrinsics necessary mainly
  82.  *  for direct keyboard input.
  83.  */
  84. #pragma intrinsic COMMAND MPE_COMMAND
  85. #pragma intrinsic FFILEINFO MPE_FFILEINFO
  86. #pragma intrinsic FCONTROL MPE_FCONTROL
  87. #pragma intrinsic FDEVICECONTROL MPE_FDEVICECONTROL
  88. #pragma intrinsic FREAD MPE_FREAD
  89. #pragma intrinsic FWRITE MPE_FWRITE
  90. #pragma intrinsic FRENAME MPE_FRENAME
  91. #pragma intrinsic HPCIPUTVAR MPE_PUTVAR
  92.  
  93. #define REVERSE 1       /* reverse video (or standout) attribute */
  94. #define HIGH 2          /* high intensity (or underline) attribute */
  95. #define NORMAL 3        /* normal video attribute */
  96.  
  97. /*
  98.  * The following variables store the appropriate escape sequences for
  99.  *  the current terminal type. This is not very elegant coding, although
  100.  *  the problem with global variables is at least restricted to just this
  101.  *  one source file.
  102.  * Eventually it would be nice to implement some equivalent to a UNIX
  103.  *  termcap file...
  104.  */
  105. static char *t_flash1;  /* start flash attribute */
  106. static char *t_flash0;  /* end flash attribute */
  107. static char *t_block1;  /* start block attribute */
  108. static char *t_block0;  /* end block attribute */
  109. static char *t_eol;     /* erase to end of line */
  110. static char *t_insline; /* insert line */
  111. static char *t_delline; /* delete line */
  112. static char *t_inschar; /* insert character */
  113. static char *t_delchar; /* delete character */
  114. static char *t_defwind; /* define scrollable window */
  115. static char *t_scrup;   /* scroll window up */
  116. static char *t_scrdown; /* scroll window down */
  117. static char *t_cp;      /* cursor positioning */
  118. static int   t_cpoff;   /* cursor positioning offset */
  119. static int   t_hptinit; /* set up HP (700/41) terminal function keys? */
  120.  
  121. /*
  122.  * Under MPE/XL, text files can either have variable length lines or fixed
  123.  *  length lines. For most purposes (such as program source files) using
  124.  *  variable length lines saves wasting space. However, MPE/XL insists that
  125.  *  job files have fixed length lines. Therefore, the editor must be able to
  126.  *  create either kind of file.
  127.  * This is achieved by checking the name of the executing program: DTE implies
  128.  *  variable length lines, DTEJ implies fixed length lines.
  129.  * The "g_job_file" variable is set TRUE if we are working with job files.
  130.  */
  131. int g_job_file;
  132.  
  133. /*
  134.  * the following variable determines the size of the memory buffer used. It
  135.  *  is set to something reasonable in main.
  136.  */
  137. static int g_space = 0;
  138.  
  139. /*
  140.  * Name:    myfflush
  141.  * Purpose: To flush any pending characters in the output buffer.
  142.  * Date:    September 3, 1990
  143.  */
  144. static void myfflush()
  145. {
  146.     fflush(stdout);
  147. }
  148.  
  149. /*
  150.  * Name:    myputchar
  151.  * Purpose: To output a single character to the display device.
  152.  * Date:    September 3, 1990
  153.  * Passed:  c:  the character to be output
  154.  */
  155. static void myputchar(c)
  156. char c;
  157. {
  158.     putc(c, stdout);
  159. }
  160.  
  161. /*
  162.  * Name:    myputs
  163.  * Purpose: To output a character string to the display device.
  164.  * Date:    November 6, 1989
  165.  * Passed:  s:  the character string to be output
  166.  */
  167. static void myputs(s)
  168. char *s;
  169. {
  170.     while (*s) {
  171.         myputchar(*s++);
  172.     }
  173. }
  174.  
  175. /*
  176.  * Name:    error
  177.  * Purpose: To report an error, and usually make the user type <ESC> before
  178.  *           continuing.
  179.  * Date:    October 10, 1989
  180.  * Passed:  kind:   an indication of how serious the error was:
  181.  *                      TEMP:    merely a message, do not wait for <ESC>
  182.  *                      DIAG:    merely a message, but make sure user sees it
  183.  *                      WARNING: error, but editor can continue after <ESC>
  184.  *                      FATAL:   abort the editor!
  185.  *          format: printf format string for any arguments that follow
  186.  *          ...:    arguments to be printed
  187.  * Notes:   This function should be system independent; that is the whole
  188.  *           point of the "stdarg" philosophy. However, two of the systems
  189.  *           I have used implemented "stdarg" incompatibly, and some older
  190.  *           systems may not support the "stdarg" macros at all...
  191.  */
  192. void error(kind, va_alist)
  193. int kind;
  194. va_dcl
  195. {
  196.     char *format;           /* printf format string for error message */
  197.     va_list argptr;         /* used to access various arguments */
  198.     char buff[MAX_COLS];    /* somewhere to store error before printing */
  199.     int c;                  /* character entered by user to continue */
  200.  
  201.     /*
  202.      * obtain the first two arguments
  203.      */
  204.     va_start(argptr);
  205.     format = va_arg(argptr, char *);
  206.  
  207.     /*
  208.      * tell the user what kind of an error it is
  209.      */
  210.     switch (kind) {
  211.     case FATAL:
  212.         strcpy(buff, "Fatal error: ");
  213.         break;
  214.     case WARNING:
  215.         strcpy(buff, "Warning: ");
  216.         break;
  217.     case DIAG:
  218.     case TEMP:
  219.         strcpy(buff, "");
  220.         break;
  221.     }
  222.  
  223.     /*
  224.      * prepare the error message itself
  225.      */
  226.     vsprintf(buff + strlen(buff), format, argptr);
  227.     va_end(argptr);
  228.  
  229.     /*
  230.      * tell the user how to continue editing if necessary
  231.      */
  232.     if (kind == WARNING || kind == DIAG) {
  233.         strcat(buff, ": type <ESC>");
  234.     }
  235.  
  236.     /*
  237.      * output the error message
  238.      */
  239.     set_prompt(buff, 1);
  240.  
  241.     if (kind == FATAL) {
  242.         /*
  243.          * no point in making the user type <ESC>, since the program is
  244.          *  about to abort anyway...
  245.          */
  246.         terminate();
  247.         exit(1);
  248.     }
  249.     else if (kind != TEMP) {
  250.         /*
  251.          * If necessary, force the user to acknowledge the error by
  252.          *  typing <ESC> (or ^U).
  253.          * This prevents any extra commands the user has entered from
  254.          *  causing problems after an error may have made them inappropriate.
  255.          */
  256.         while ((c=c_input()) != 27 && c != CONTROL('U')) {
  257.             set_prompt(buff, 1);
  258.         }
  259.     }
  260. }
  261.  
  262. /*
  263.  * Name:    termset
  264.  * Purpose: To set an MPE variable to record the terminal type for future
  265.  *           invocations of the editor.
  266.  * Passed:  name:  the name of the terminal (termcap style!)
  267.  * Date:    November 10, 1989
  268.  */
  269. static void termset(name)
  270. char *name;
  271. {
  272.     int length;   /* length of terminal name */
  273.     int status;   /* status of MPE variable setting command */
  274.  
  275.     length = strlen(name);
  276.     MPE_PUTVAR("DTETERM", &status, 2, name, 11, &length);
  277. }
  278.  
  279. /*
  280.  * Name:    main
  281.  * Purpose: To do any system dependent command line argument processing,
  282.  *           and then call the main editor function.
  283.  * Date:    November 10, 1989
  284.  * Passed:  argc:   number of command line arguments
  285.  *          argv:   text of command line arguments
  286.  * Notes:   The MPE/XL version needs to determine the type of terminal
  287.  *           currently in use.
  288.  */
  289. void main(argc, argv)
  290. int argc;
  291. char *argv[];
  292. {
  293.     char termc;                     /* terminal identifier */
  294.     char *term;                     /* terminal name */
  295.     static char std_buff[4096];     /* output buffer */
  296.  
  297.     /*
  298.      * allocate a large output buffer
  299.      */
  300.     setvbuf(stdout, std_buff, _IOFBF, 4096);
  301.  
  302.     /*
  303.      * work out whether we are working with job files or normal files.
  304.      */
  305.     if (mystrcmpi("DTEJ", argv[0]) == 0) {
  306.         g_job_file = TRUE;
  307.     }
  308.     else {
  309.         g_job_file = FALSE;
  310.     }
  311.  
  312.     /*
  313.      * see if user specified buffer size
  314.      */
  315.     if (argc > 1 && mystrcmpi("-s", argv[1]) == 0) {
  316.         g_space = atoi(argv[1]+2);
  317.         ++argv;
  318.         --argc;
  319.     }
  320.  
  321.     /*
  322.      * ensure space is reasonable
  323.      */
  324.     if (g_space < 1000) {
  325.         g_space = 100000;   /* enough for program source code files */
  326.     }
  327.  
  328.     /*
  329.      * set path for the help file
  330.      */
  331.     hw_copy_path(argv[0], "DTEHELP", g_status.help_file);
  332.  
  333.     /*
  334.      * Check DTETERM system variable to see if a terminal kind has already
  335.      *  been set.
  336.      */
  337.     termc = '?';
  338.     if ((term = getenv("DTETERM")) != NULL) {
  339.         if (strcmp(term, "vt100") == 0) {
  340.             termc = 'v';
  341.         }
  342.         else if (strcmp(term, "vt220") == 0) {
  343.             termc = '2';
  344.         }
  345.         else if (strcmp(term, "tvi920") == 0) {
  346.             termc = 't';
  347.         }
  348.         else if (strcmp(term, "tvi925") == 0) {
  349.             termc = 'h';
  350.         }
  351.         else if (strcmp(term, "hp2392a") == 0) {
  352.             termc = '9';
  353.         }
  354.         else if (strcmp(term, "dmp") == 0) {
  355.             termc = 'd';
  356.         }
  357.     }
  358.  
  359.     /*
  360.      * If no system variable, then ask the user to choose from a menu
  361.      *  of supported terminals.
  362.      */
  363.     if (termc == '?') {
  364.         for (;;) {
  365.             myputs("Terminals Available:\n\n");
  366.             myputs("    v) VT100 (VISUAL 300)\n");
  367.             myputs("    2) VT220\n");
  368.             myputs("    h) Televideo 925 (HP700/41)\n");
  369.             myputs("    9) HP 2392A\n");
  370.             myputs("    t) Televideo 920 (PROCOMM)\n");
  371.             myputs("    d) DMP\n\n");
  372.  
  373.             myputs("Selection: ");
  374.             myfflush();
  375.             switch (getchar()) {
  376.                 case 'v':
  377.                 case 'V':
  378.                     termc = 'v';
  379.                     termset("vt100");
  380.                     break;
  381.                 case '2':
  382.                     termc = '2';
  383.                     termset("vt220");
  384.                     break;
  385.                 case 'h':
  386.                 case 'H':
  387.                     termc = 'h';
  388.                     termset("tvi925");
  389.                     break;
  390.                 case 't':
  391.                 case 'T':
  392.                     termc = 't';
  393.                     termset("tvi920");
  394.                     break;
  395.                 case '9':
  396.                     termc = '9';
  397.                     termset("hp2392a");
  398.                     break;
  399.                 case 'd':
  400.                     termc = 'd';
  401.                     termset("dmp");
  402.                     break;
  403.                 default:
  404.                     fflush(stdin);
  405.                     continue;
  406.             }
  407.             break;
  408.         }
  409.     }
  410.  
  411.     /*
  412.      * now terminal type is known, set the appropriate escape sequences
  413.      */
  414.     switch (termc) {
  415.     case 'v':
  416.         t_flash1 = "\033[1m";       /* start flash attribute */
  417.         t_flash0 = "\033[m";        /* end flash attribute */
  418.         t_block1 = "\033[7m";       /* start block attribute */
  419.         t_block0 = "\033[m";        /* end block attribute */
  420.         t_eol = "\033[K";           /* erase to end of line */
  421.         t_insline = NULL;           /* insert line */
  422.         t_delline = NULL;           /* delete line */
  423.         t_inschar = NULL;           /* insert character */
  424.         t_delchar = NULL;           /* delete character */
  425.         t_defwind = "\033[%d;%dr";  /* define scrollable window */
  426.         t_scrup = "\033M";          /* scroll window up */
  427.         t_scrdown = "\n";           /* scroll window down */
  428.         t_cp = "\033[%d;%dH";       /* cursor positioning */
  429.         t_cpoff = 1;                /* cursor positioning offset */
  430.         break;
  431.     case '2':
  432.         t_flash1 = "\033[1m";       /* start flash attribute */
  433.         t_flash0 = "\033[m";        /* end flash attribute */
  434.         t_block1 = "\033[7m";       /* start block attribute */
  435.         t_block0 = "\033[m";        /* end block attribute */
  436.         t_eol = "\033[K";           /* erase to end of line */
  437.         t_insline = "\033[L";       /* insert line */
  438.         t_delline = "\033[M";       /* delete line */
  439.         t_inschar = NULL;           /* insert character */
  440.         t_delchar = "\033[P";       /* delete character */
  441.         t_defwind = "\033[%d;%dr";  /* define scrollable window */
  442.         t_scrup = "\033M";          /* scroll window up */
  443.         t_scrdown = "\033D";        /* scroll window down */
  444.         t_cp = "\033[%d;%dH";       /* cursor positioning */
  445.         t_cpoff = 1;                /* cursor positioning offset */
  446.         break;
  447.     case 'h':
  448.         t_flash1 = NULL;            /* start flash attribute */
  449.         t_flash0 = NULL;            /* end flash attribute */
  450.         t_block1 = NULL;            /* start block attribute */
  451.         t_block0 = NULL;            /* end block attribute */
  452.         t_eol = "\033T";            /* erase to end of line */
  453.         t_insline = "\033E";        /* insert line */
  454.         t_delline = "\033R";        /* delete line */
  455.         t_inschar = "\033Q";        /* insert character */
  456.         t_delchar = "\033W";        /* delete character */
  457.         t_defwind = NULL;           /* define scrollable window */
  458.         t_scrup = NULL;             /* scroll window up */
  459.         t_scrdown = NULL;           /* scroll window down */
  460.         t_cp = "\033=%c%c";         /* cursor positioning */
  461.         t_cpoff = 32;               /* cursor positioning offset */
  462.         t_hptinit = TRUE;           /* initialize terminal keys */
  463.         break;
  464.     case 't':
  465.         t_flash1 = "\033l";         /* start flash attribute */
  466.         t_flash0 = "\033m";         /* end flash attribute */
  467.         t_block1 = "\033j";         /* start block attribute */
  468.         t_block0 = "\033k";         /* end block attribute */
  469.         t_eol = "\033T";            /* erase to end of line */
  470.         t_insline = "\033E";        /* insert line */
  471.         t_delline = "\033R";        /* delete line */
  472.         t_inschar = "\033Q";        /* insert character */
  473.         t_delchar = "\033W";        /* delete character */
  474.         t_defwind = NULL;           /* define scrollable window */
  475.         t_scrup = NULL;             /* scroll window up */
  476.         t_scrdown = NULL;           /* scroll window down */
  477.         t_cp = "\033=%c%c";         /* cursor positioning */
  478.         t_cpoff = 32;               /* cursor positioning offset */
  479.         break;
  480.     case '9':
  481.         t_flash1 = NULL;            /* start flash attribute */
  482.         t_flash0 = NULL;            /* end flash attribute */
  483.         t_block1 = NULL;            /* start block attribute */
  484.         t_block0 = NULL;            /* end block attribute */
  485.         t_eol = "\033K";            /* erase to end of line */
  486.         t_insline = "\033L";        /* insert line */
  487.         t_delline = "\033M";        /* delete line */
  488.         t_inschar = NULL;           /* insert character */
  489.         t_delchar = "\033P";        /* delete character */
  490.         t_defwind = NULL;           /* define scrollable window */
  491.         t_scrup = NULL;             /* scroll window up */
  492.         t_scrdown = NULL;           /* scroll window down */
  493.         t_cp = "\033&a%dy%dC";      /* cursor positioning */
  494.         t_cpoff = 0;                /* cursor positioning offset */
  495.         break;
  496.     case 'd':
  497.         t_flash1 = "\020";          /* start flash attribute */
  498.         t_flash0 = "\016";          /* end flash attribute */
  499.         t_block1 = "\022";          /* start block attribute */
  500.         t_block0 = "\016";          /* end block attribute */
  501.         t_eol = "\027";             /* erase to end of line */
  502.         t_insline = "\030";         /* insert line */
  503.         t_delline = "\031";         /* delete line */
  504.         t_inschar = "\025";         /* insert character */
  505.         t_delchar = "\026";         /* delete character */
  506.         t_defwind = "\013%c%c";     /* define scrollable window */
  507.         t_scrup = "\005";           /* scroll window up */
  508.         t_scrdown = "\006";         /* scroll window down */
  509.         t_cp = "\002%c%c";          /* cursor positioning */
  510.         t_cpoff = 32;               /* cursor positioning offset */
  511.         break;
  512.     }
  513.  
  514.     /*
  515.      * now start up the main editor
  516.      */
  517.     editor(argc, argv);
  518. }
  519.  
  520. /*
  521.  * Name:    hw_attr
  522.  * Purpose: To select a new attribute on the terminal.
  523.  * Date:    October 10, 1989
  524.  * Passed:  attr: the desired attribute
  525.  */
  526. static void hw_attr(attr)
  527. char attr;
  528. {
  529.     static int old_att = -1;         /* existing attribute */
  530.  
  531.     /*
  532.      * If there has been no change, then ignore the call (actually this
  533.      *  should never happen, since hw_attr is only called when the
  534.      *  attribute HAS changed...
  535.      */
  536.     if (old_att == attr) {
  537.         return;
  538.     }
  539.  
  540.     /*
  541.      * end the current attribute
  542.      */
  543.     if (old_att != g_display.normal) {
  544.         if (old_att == g_display.flash) {
  545.             if (t_flash0) {
  546.                 myputs(t_flash0);
  547.             }
  548.         }
  549.         else if (old_att == g_display.block) {
  550.             if (t_block0) {
  551.                 myputs(t_block0);
  552.             }
  553.         }
  554.     }
  555.  
  556.     /*
  557.      * set the new attribute
  558.      */
  559.     if (attr == g_display.flash) {
  560.         if (t_flash1) {
  561.             myputs(t_flash1);
  562.         }
  563.     }
  564.     else if (attr == g_display.block) {
  565.         if (t_block1) {
  566.             myputs(t_block1);
  567.         }
  568.     }
  569.  
  570.     /*
  571.      * record new attribute for next time
  572.      */
  573.     old_att = attr;
  574. }
  575.  
  576. /*
  577.  * Name:    att_stuff
  578.  * Purpose: To make sure that the attribute is set to normal before commands
  579.  *           such as clear to end of line are executed.
  580.  * Date:    October 10, 1989
  581.  * Passed:  [g_display.attr]:   the current attribute
  582.  *          [g_display.normal]: the normal attribute
  583.  * Returns: [g_display.attr]:   set to normal
  584.  * Notes:   This function is necessary because some terminals clear to
  585.  *           spaces using the current attribute, while others clear to
  586.  *           normal spaces. Unfortunately terminfo does not seem to record
  587.  *           this distinction.
  588.  */
  589. static void att_stuff()
  590. {
  591.     if (g_display.attr != g_display.normal) {
  592.         hw_attr(g_display.normal);
  593.         g_display.attr = g_display.normal;
  594.     }
  595. }
  596.  
  597. /*
  598.  * Name:    att_check
  599.  * Purpose: To check that the attribute required for the next character is
  600.  *           the one currently in effect, and set it if different.
  601.  * Date:    October 10, 1989
  602.  * Passed:  [g_display.attr]:  the current attribute
  603.  *          [g_status.wanted]: the required attribute
  604.  * Returns: [g_display.attr]:  the newly set attribute
  605.  */
  606. void att_check()
  607. {
  608.     if (g_display.attr != g_status.wanted) {
  609.         hw_attr(g_status.wanted);
  610.         g_display.attr = g_status.wanted;
  611.     }
  612. }
  613.  
  614. /*
  615.  * Name:    hw_xygoto
  616.  * Purpose: To move the cursor to a new position on the screen.
  617.  * Date:    October 10, 1989
  618.  * Passed:  [g_display.line]: the required line
  619.  *          [g_display.col]:  the required column
  620.  */
  621. void hw_xygoto()
  622. {
  623.     char buff[20];  /* for cursor positioning command */
  624.  
  625.     sprintf(buff, t_cp, g_display.line+t_cpoff, g_display.col+t_cpoff);
  626.     myputs(buff);
  627. }
  628.  
  629. /*
  630.  * The following locally global variables are used to keep track of the
  631.  *  character in the bottom right corner of the screen.
  632.  * It is not safe to write this character, since most terminals will
  633.  *  scroll the whole screen up a line after writing it.
  634.  * However, if the screen is subsequently scrolled up for any reason, then
  635.  *  this character must appear on the second bottom line!
  636.  * This causes numerous complications in the code which follows...
  637.  */
  638. static char g_mem_c = 0;   /* character supposed to be at bottom right */
  639. static char g_mem_attr;    /* attribute for g_mem_c */
  640.  
  641. /*
  642.  * Name:    hw_clreol
  643.  * Purpose: To clear from the cursor to the end of the cursor line.
  644.  * Date:    October 10, 1989
  645.  * Returns: TRUE if the hardware could clear to end of line, FALSE otherwise
  646.  */
  647. int hw_clreol()
  648. {
  649.     /*
  650.      * find out if function is available, and give up if not
  651.      */
  652.     if (t_eol == NULL) {
  653.         return FALSE;
  654.     }
  655.  
  656.     /*
  657.      * clear to end of line, using normal attribute
  658.      */
  659.     att_stuff();
  660.     myputs(t_eol);
  661.  
  662.     /*
  663.      * If we just cleared the bottom line, then the bottom right character
  664.      *  was cleared too.
  665.      */
  666.     if (g_display.line == g_display.nlines-1) {
  667.         g_mem_c = 0;
  668.     }
  669.  
  670.     return TRUE;
  671. }
  672.  
  673. /*
  674.  * Name:    hw_linedel
  675.  * Purpose: To delete the cursor line, scrolling lines below up.
  676.  * Date:    October 10, 1989
  677.  * Passed:  line:  line on screen to be deleted
  678.  * Returns: TRUE if the hardware could delete the line, FALSE otherwise
  679.  */
  680. int hw_linedel(line)
  681. int line;
  682. {
  683.     /*
  684.      * check availability of function
  685.      */
  686.     if (t_delline == NULL) {
  687.         return FALSE;
  688.     }
  689.  
  690.     /*
  691.      * delete the line
  692.      */
  693.     att_stuff();
  694.     xygoto(0, line);
  695.     myputs(t_delline);
  696.  
  697.     /*
  698.      * If this caused the bottom line to move up (which will usually be
  699.      *  the case), then add the bottom right character (if any) onto the
  700.      *  second bottom line.
  701.      */
  702.     if (g_mem_c) {
  703.         if (line < g_display.nlines-1) {
  704.             xygoto(g_display.ncols-1, g_display.nlines-2);
  705.             set_attr(g_mem_attr);
  706.             c_output(g_mem_c);
  707.             g_display.col = g_display.line = -1;
  708.         }
  709.         g_mem_c = 0;
  710.     }
  711.     return TRUE;
  712. }
  713.  
  714. /*
  715.  * Name:    hw_scroll_up
  716.  * Purpose: To scroll the lines in a given region up one line.
  717.  * Date:    October 10, 1989
  718.  * Passed:  top:    the top line in the window
  719.  *          bottom: the bottom line in the window
  720.  * Returns: TRUE if terminal could scroll, FALSE otherwise
  721.  * Notes:   If this function does not exist, then insert and delete line
  722.  *           can achieve the same effect. However, insert and delete line
  723.  *           make lower windows jump, so using terminal scrolling is
  724.  *           preferable.
  725.  */
  726. int hw_scroll_up(top, bottom)
  727. int top;
  728. int bottom;
  729. {
  730.     char buff[20];   /* for window definition command */
  731.  
  732.     /*
  733.      * check if function is available
  734.      */
  735.     if (t_defwind == NULL || t_scrdown == NULL) {
  736.         return FALSE;
  737.     }
  738.  
  739.     /*
  740.      * select window to be affected
  741.      */
  742.     att_stuff();
  743.     sprintf(buff, t_defwind, top+t_cpoff, bottom+t_cpoff);
  744.     myputs(buff);
  745.     g_display.col = -1;
  746.     g_display.line = -1;
  747.  
  748.     /*
  749.      * scroll the window up
  750.      */
  751.     xygoto(0, bottom);
  752.     myputs(t_scrdown);
  753.  
  754.     /*
  755.      * don't leave a peculiar region scrolling - it confuses all sorts of
  756.      *  things!
  757.      */
  758.     sprintf(buff, t_defwind, 0+t_cpoff, g_display.nlines-1+t_cpoff);
  759.     myputs(buff);
  760.     g_display.col = -1;
  761.     g_display.line = -1;
  762.  
  763.     /*
  764.      * if the bottom line was scrolled up, then restore the old bottom
  765.      *  right character to the second bottom line
  766.      */
  767.     if (g_mem_c) {
  768.         if (bottom == g_display.nlines-1) {
  769.             xygoto(g_display.ncols-1, g_display.nlines-2);
  770.             set_attr(g_mem_attr);
  771.             c_output(g_mem_c);
  772.             g_display.col = -1;
  773.             g_display.line = -1;
  774.         }
  775.         g_mem_c = 0;
  776.     }
  777.  
  778.     return TRUE;
  779. }
  780.  
  781. /*
  782.  * Name:    hw_lineins
  783.  * Purpose: To insert a blank line above the cursor line, scrolling the
  784.  *           cursor line and lines below down.
  785.  * Date:    October 10, 1989
  786.  * Passed:  line:  line on screen to be inserted
  787.  * Returns: TRUE if the hardware could insert the line, FALSE otherwise
  788.  */
  789. int hw_lineins(line)
  790. int line;
  791. {
  792.     /*
  793.      * give up if not available
  794.      */
  795.     if (t_insline == NULL) {
  796.         return FALSE;
  797.     }
  798.  
  799.     /*
  800.      * insert the line
  801.      */
  802.     att_stuff();
  803.     xygoto(0, line);
  804.     myputs(t_insline);
  805.  
  806.     /*
  807.      * regardless of where the line was inserted, the bottom line
  808.      *  (including the bottom right character) scrolled off the screen
  809.      */
  810.     g_mem_c = 0;
  811.  
  812.     return TRUE;
  813. }
  814.  
  815. /*
  816.  * Name:    hw_scroll_down
  817.  * Purpose: To scroll the lines in a given region down one line.
  818.  * Date:    October 10, 1989
  819.  * Passed:  top:    the top line in the window
  820.  *          bottom: the bottom line in the window
  821.  * Returns: TRUE if terminal could scroll, FALSE otherwise
  822.  * Notes:   If this function does not exist, then insert and delete line
  823.  *           can achieve the same effect. However, insert and delete line
  824.  *           make lower windows jump, so using terminal scrolling is
  825.  *           preferable.
  826.  */
  827. int hw_scroll_down(top, bottom)
  828. int top;
  829. int bottom;
  830. {
  831.     char buff[20];  /* for define window command */
  832.  
  833.     /*
  834.      * check if function is available
  835.      */
  836.     if (t_defwind == NULL || t_scrup == NULL) {
  837.         return FALSE;
  838.     }
  839.  
  840.     /*
  841.      * select window to be affected
  842.      */
  843.     att_stuff();
  844.     sprintf(buff, t_defwind, top+t_cpoff, bottom+t_cpoff);
  845.     myputs(buff);
  846.     g_display.col = -1;
  847.     g_display.line = -1;
  848.  
  849.     /*
  850.      * scroll the window up
  851.      */
  852.     xygoto(0, top);
  853.     myputs(t_scrup);
  854.  
  855.     /*
  856.      * don't leave a peculiar region scrolling - it confuses all sorts of
  857.      *  things!
  858.      */
  859.     sprintf(buff, t_defwind, 0+t_cpoff, g_display.nlines-1+t_cpoff);
  860.     myputs(buff);
  861.     g_display.col = -1;
  862.     g_display.line = -1;
  863.  
  864.     /*
  865.      * if the region included the bottom line, then the bottom right
  866.      *  character moved off the screen altogether
  867.      */
  868.     if (bottom == g_display.nlines-1) {
  869.         g_mem_c = 0;
  870.     }
  871.  
  872.     return TRUE;
  873. }
  874.  
  875. /*
  876.  * Name:    hw_c_avail
  877.  * Purpose: To test whether or not a character has been typed by the user.
  878.  * Date:    October 10, 1989
  879.  * Returns: TRUE if user typed something, FALSE otherwise
  880.  * Notes:   No simple way to check under MPE/XL, so just pretend the
  881.  *           user waited until the screen was fully updated.
  882.  */
  883. int hw_c_avail()
  884. {
  885.     return FALSE;
  886. }
  887.  
  888. /*
  889.  * Name:    hw_c_input
  890.  * Purpose: To input a character from the user, without echo, waiting if
  891.  *           nothing has been typed yet.
  892.  * Date:    October 10, 1989
  893.  * Returns: the character the user typed
  894.  * Notes:   A return value of 0 means that what the user typed should be
  895.  *           ignored.
  896.  */
  897. int hw_c_input()
  898. {
  899.     char key;  /* the key the user typed */
  900.  
  901.     myfflush();  /* first display everything that is pending */
  902.  
  903.     MPE_FREAD(_mpe_fileno(0), &key, -1);
  904.     return key;
  905. }
  906.  
  907. /*
  908.  * Name:    hw_c_output
  909.  * Purpose: To output a character, using the current attribute, at the
  910.  *           current screen position.
  911.  * Date:    October 10, 1989
  912.  * Notes:   If the current screen position is the bottom right corner, then
  913.  *           we do not write the character, but merely store it away for
  914.  *           later. (See explanation above.)
  915.  */
  916. void hw_c_output(c)
  917. int c;
  918. {
  919.     if (g_display.line == g_display.nlines-1 &&
  920.             g_display.col == g_display.ncols-1) {
  921.         g_mem_c = c;
  922.         g_mem_attr = g_status.wanted;
  923.         return;
  924.     }
  925.     att_check();
  926.     myputchar(c);
  927. }
  928.  
  929. /*
  930.  * Name:    hw_terminate
  931.  * Purpose: To restore the terminal to a safe state prior to leaving the
  932.  *           editor.
  933.  * Date:    October 10, 1989
  934.  */
  935. void hw_terminate()
  936. {
  937.     unsigned short status;  /* error status of MPE intrinsic */
  938.     unsigned short command; /* device control command number */
  939.  
  940.     /*
  941.      * ensure no windows are left (it is annoying to exit the editor and
  942.      *  then find that only the bottom 4 lines of the screen can be used
  943.      *  for other purposes!
  944.      */
  945.     window_scroll_up(0, g_display.nlines-1);
  946.  
  947.     /*
  948.      * leave the editor text on the screen, but move the cursor to the
  949.      *  bottom line.
  950.      */
  951.     xygoto(0, g_display.nlines-1);
  952.     att_stuff();
  953.  
  954.     /*
  955.      * restore text mode with echo
  956.      */
  957.     command = 1;
  958.     MPE_FDEVICECONTROL(_mpe_fileno(0), &command, 1, 192, 26, 2, &status);
  959.     command = 0;
  960.     MPE_FDEVICECONTROL(_mpe_fileno(0), &command, 1, 192, 32, 2, &status);
  961.     MPE_FCONTROL(_mpe_fileno(0), 12, &status);
  962.     MPE_FCONTROL(_mpe_fileno(0), 26, &status);
  963.     MPE_FCONTROL(_mpe_fileno(0), 15, &status);
  964.  
  965.     printf("dte version %sC for MPE/XL %s files\n", VERSION,
  966.             g_job_file ? "job" : "program");
  967. }
  968.  
  969. /*
  970.  * If the MPE/XL C library contained a function to do overlapping moves
  971.  *  correctly, then only one text buffer would be required. However,
  972.  *  copying individual bytes was too slow, and it was much faster to
  973.  *  copy everything to an extra buffer, and then back to the destination!
  974.  */
  975. static char *g_buffer;
  976.  
  977. /*
  978.  * Name:    hw_initialize
  979.  * Purpose: To initialize the display ready for editor use.
  980.  * Date:    October 10, 1989
  981.  * Notes:   Typed characters (including ^S and ^Q and ^\) must all be
  982.  *           returned without echoing!
  983.  */
  984. void hw_initialize()
  985. {
  986.     unsigned short result;   /* result of setting up terminal commands */
  987.     unsigned short command;  /* MPE device control command number */
  988.     char buff[20];           /* for cursor positioning command */
  989.  
  990.     /*
  991.      * allocate space for the screen image
  992.      */
  993.     if ((g_screen = (screen_lines *)malloc(MAX_LINES * sizeof(screen_lines)))
  994.             == NULL) {
  995.         printf("no memory for screen image\n");
  996.         exit(1);
  997.     }
  998.  
  999.     /*
  1000.      * set up terminal screen size
  1001.      */
  1002.     g_display.ncols = 80;
  1003.     g_display.nlines = 24;
  1004.  
  1005.     /*
  1006.      * work out the length of the cursor addressing command, so we can
  1007.      *  choose the quickest way of getting anywhere
  1008.      */
  1009.     sprintf(buff, t_cp, 24+t_cpoff, 80+t_cpoff);
  1010.     g_display.ca_len = strlen(buff);
  1011.  
  1012.     /*
  1013.      * set up raw input with no echo etc
  1014.      */
  1015.     MPE_FCONTROL(_mpe_fileno(0), 13, &result);
  1016.     MPE_FCONTROL(_mpe_fileno(0), 14, &result);
  1017.     command = 0;
  1018.     MPE_FDEVICECONTROL(_mpe_fileno(0), &command, 1, 192, 26, 2, &result);
  1019.     command = 0;
  1020.     MPE_FDEVICECONTROL(_mpe_fileno(0), &command, 1, 192, 32, 2, &result);
  1021.     MPE_FCONTROL(_mpe_fileno(0), 27, &result);
  1022.  
  1023.     /*
  1024.      * set up video attributes
  1025.      */
  1026.     g_display.block = REVERSE;
  1027.     g_display.flash = HIGH;
  1028.     g_display.normal = NORMAL;
  1029.     g_display.attr = NORMAL;
  1030.     hw_attr(NORMAL);
  1031.  
  1032.     if (t_hptinit) {
  1033.         /*
  1034.          * initialize function keys, select terminal emulation
  1035.          */
  1036.         printf("\033~\"\033~ ");
  1037.         printf("\033z#%c\177", CONTROL('G'));
  1038.         printf("\033z&%c\177", CONTROL('I'));
  1039.         printf("\033z+%c\177", CONTROL('E'));
  1040.         printf("\033z,%c\177", CONTROL('X'));
  1041.         printf("\033z-%c\177", CONTROL('S'));
  1042.         printf("\033z.%c\177", CONTROL('D'));
  1043.         printf("\033z/%c%c\177", CONTROL('Q'), CONTROL('E'));
  1044.         printf("\033~$\033\"\033'");
  1045.         fflush(stdout);
  1046.     }
  1047.  
  1048.     /*
  1049.      * allocate space for the main text buffer and the copying buffer
  1050.      */
  1051.     if ((g_status.start_mem = (char *)malloc(g_space)) == NULL) {
  1052.         error(FATAL, "out of memory for text");
  1053.     }
  1054.     g_status.max_mem = g_status.start_mem + g_space;
  1055.     if ((g_buffer = (char *)malloc(g_space)) == NULL) {
  1056.         error(FATAL, "out of memory for buffer");
  1057.     }
  1058. }
  1059.  
  1060. /*
  1061.  * Name:    hw_move
  1062.  * Purpose: To move data from one place to another as efficiently as
  1063.  *           possible.
  1064.  * Date:    October 10, 1989
  1065.  * Passed:  dest:   where to copy to
  1066.  *          source: where to copy from
  1067.  *          number: number of bytes to copy
  1068.  * Notes:   moves may be (usually will be) overlapped
  1069.  */
  1070. void hw_move(dest, source, number)
  1071. text_ptr dest;
  1072. text_ptr source;
  1073. long number;
  1074. {
  1075.     if (number < 0) {
  1076.         /*
  1077.          * this should never happen...
  1078.          */
  1079.         error(WARNING, "negative move - contact Douglas Thomson!");
  1080.     }
  1081.     else if (source == dest) {
  1082.         /*
  1083.          * nothing to be done
  1084.          */
  1085.         ;
  1086.     }
  1087.     else {
  1088.         /*
  1089.          * no overlapping move available, so copy to buffer and back
  1090.          */
  1091.         memcpy(g_buffer, source, number);
  1092.         memcpy(dest, g_buffer, number);
  1093.     }
  1094. }
  1095.  
  1096. /*
  1097.  * Name:    hw_backspace
  1098.  * Purpose: To move the cursor left one position.
  1099.  * Date:    October 10, 1989
  1100.  * Returns: TRUE if the hardware could backspace, FALSE otherwise
  1101.  * Notes:   This function is used where deletion requires a backspace,
  1102.  *           space, backspace. If the terminal can backspace, this may
  1103.  *           be much faster than using cursor addressing.
  1104.  */
  1105. int hw_backspace()
  1106. {
  1107.     myputchar('\b');
  1108.     return TRUE;
  1109. }
  1110.  
  1111. /*
  1112.  * Name:    hw_c_insert
  1113.  * Purpose: To insert a blank character under the cursor.
  1114.  * Date:    October 10, 1989
  1115.  * Returns: TRUE if the hardware could insert the space, FALSE otherwise
  1116.  * Notes:   This function is used where the user has just typed a character
  1117.  *           in the middle of a line in insert mode. If it is available, it
  1118.  *           saves having to redraw the entire remainder of the line.
  1119.  *          No assumptions are made about the contents or attribute of the
  1120.  *           inserted character.
  1121.  */
  1122. int hw_c_insert()
  1123. {
  1124.     if (t_inschar == NULL) {
  1125.         return FALSE;
  1126.     }
  1127.  
  1128.     myputs(t_inschar);
  1129.  
  1130.     if (g_mem_c && g_display.line == g_display.nlines-1) {
  1131.         g_mem_c = 0;
  1132.     }
  1133.  
  1134.     return TRUE;
  1135. }
  1136.  
  1137. /*
  1138.  * Name:    hw_c_delete
  1139.  * Purpose: To delete the character under the cursor.
  1140.  * Date:    October 10, 1989
  1141.  * Returns: TRUE if the hardware could delete the character, FALSE otherwise
  1142.  * Notes:   This function is used where the user has deleted a character
  1143.  *           in the middle of a line. If it is available, it saves having to
  1144.  *           redraw the entire remainder of the line.
  1145.  *          The rightmost character on the line after the delete is assumed
  1146.  *           to be a space character with normal attribute.
  1147.  */
  1148. int hw_c_delete()
  1149. {
  1150.     if (t_delchar == NULL) {
  1151.         return FALSE;
  1152.     }
  1153.  
  1154.     myputs(t_delchar);
  1155.  
  1156.     /*
  1157.      * bottom right corner character could need to reappear one
  1158.      *  character in from the right
  1159.      */
  1160.     if (g_mem_c && g_display.line == g_display.nlines-1) {
  1161.         if (g_display.col < g_display.ncols-2) {
  1162.             xygoto(g_display.ncols-2, g_display.nlines-1);
  1163.             set_attr(g_mem_attr);
  1164.             c_output(g_mem_c);
  1165.             g_display.col = g_display.line = -1;
  1166.         }
  1167.         g_mem_c = 0;
  1168.     }
  1169.  
  1170.     return TRUE;
  1171. }
  1172.  
  1173. /*
  1174.  * Name:    hw_rename
  1175.  * Purpose: To rename a disk file to a new name.
  1176.  * Date:    October 10, 1989
  1177.  * Passed:  old: current file name
  1178.  *          new: new desired file name
  1179.  * Returns: OK if rename succeeded, ERROR if any problem
  1180.  */
  1181. int hw_rename(old, new)
  1182. char *old;
  1183. char *new;
  1184. {
  1185.     return rename(old, new);
  1186. }
  1187.  
  1188. /*
  1189.  * Name:    hw_fattrib
  1190.  * Purpose: To determine the current file attributes.
  1191.  * Date:    October 17, 1989
  1192.  * Passed:  name: name of file to be checked
  1193.  * Returns: current read/write/execute etc attributes of the file, or
  1194.  *          ERROR if file did not exist etc.
  1195.  */
  1196. int hw_fattrib(name)
  1197. char *name;
  1198. {
  1199.     FILE *fp;
  1200.  
  1201.     /*
  1202.      * The MPE/XL implementation is very simple-minded.
  1203.      */
  1204.     if ((fp = fopen(name, "r")) == NULL) {
  1205.         return ERROR;
  1206.     }
  1207.     fclose(fp);
  1208.     return OK;
  1209. }
  1210.  
  1211. /*
  1212.  * Name:    hw_set_fattrib
  1213.  * Purpose: To set the current file attributes.
  1214.  * Date:    October 17, 1989
  1215.  * Passed:  name:   name of file to be changed
  1216.  *          attrib: the required attributes
  1217.  * Returns: new read/write/execute etc attributes of the file, or
  1218.  *          ERROR if file did not exist etc.
  1219.  * Notes:   If "attrib" is ERROR, then do not change attributes.
  1220.  */
  1221. int hw_set_fattrib(name, attrib)
  1222. char *name;
  1223. int attrib;
  1224. {
  1225.     if (attrib == ERROR) {
  1226.         return ERROR;
  1227.     }
  1228.     return OK;
  1229. }
  1230.  
  1231. /*
  1232.  * Name:    hw_unlink
  1233.  * Purpose: To delete a file, regardless of access modes.
  1234.  * Date:    October 17, 1989
  1235.  * Passed:  name:   name of file to be removed
  1236.  * Returns: OK if file could be removed
  1237.  *          ERROR otherwise
  1238.  */
  1239. int hw_unlink(name)
  1240. char *name;
  1241. {
  1242.     FILE *fp;
  1243.  
  1244.     /*
  1245.      * Open file in such a way that it will be removed when closed -
  1246.      *  seems an odd way to do things, but the MPE/XL library
  1247.      *  has no unlink().
  1248.      */
  1249.     if ((fp = fopen(name, "r Df4")) == NULL) {
  1250.         return ERROR;
  1251.     }
  1252.     return fclose(fp);
  1253. }
  1254.  
  1255. /*
  1256.  * Name:    hw_printable
  1257.  * Purpose: To determine whether or not a character is printable on the
  1258.  *           current hardware.
  1259.  * Date:    October 18, 1989
  1260.  * Passed:  c: the character to be tested
  1261.  * Returns: TRUE if c is a visible character, FALSE otherwise
  1262.  * Notes:   This is hardware dependent so that machines like the IBM PC can
  1263.  *           edit files containing graphics characters.
  1264.  */
  1265. int hw_printable(c)
  1266. int c;
  1267. {
  1268.    return (c >= 32 && c < 127);
  1269. }
  1270.  
  1271. /*
  1272.  * Name:    hw_load
  1273.  * Purpose: To load a file into the text buffer.
  1274.  * Date:    November 11, 1989
  1275.  * Passed:  name:  name of disk file
  1276.  *          start: first character in text buffer
  1277.  *          limit: last available character in text buffer
  1278.  *          end:   last character (+1) from the file
  1279.  * Returns: OK, or ERROR if anything went wrong
  1280.  * Notes:   All error messages are displayed here, so the caller should
  1281.  *           neither tell the user what is happening, nor print an error
  1282.  *           message if anything goes wrong.
  1283.  *          This function is in the hardware dependent module because
  1284.  *           some computers require non-standard open parameters...
  1285.  */
  1286. int hw_load(name, start, limit, end)
  1287. char *name;
  1288. text_ptr start;
  1289. text_ptr limit;
  1290. text_ptr *end;
  1291. {
  1292.     int fd;             /* file being read */
  1293.     int length;         /* number of bytes actually read */
  1294.     short code;         /* file type code */
  1295.     foptions foption;   /* for determining if file is ASCII */
  1296.  
  1297.     /*
  1298.      * try reading the file, trimming trailing space and editor line
  1299.      *  numbers.
  1300.      */
  1301.     if ((fd = open(name, O_RDONLY|O_MPEOPTS, 0, "Tm")) == ERROR) {
  1302.         error(WARNING, "File '%s' not found", name);
  1303.         return ERROR;
  1304.     }
  1305.  
  1306.     /*
  1307.      * check file is ASCII text file
  1308.      */
  1309.     code = -1;
  1310.     MPE_FFILEINFO(_mpe_fileno(fd), 8, &code);
  1311.     if (code != 0) {
  1312.         close(fd);
  1313.         error(WARNING, "cannot edit this file type (%d)", code);
  1314.         return ERROR;
  1315.     }
  1316.     foption.fs.ascii = 0;
  1317.     MPE_FFILEINFO(_mpe_fileno(fd), 2, &foption.fv);
  1318.     if (foption.fs.ascii != 1) {
  1319.         close(fd);
  1320.         error(WARNING, "only ASCII text files can be edited");
  1321.         return ERROR;
  1322.     }
  1323.  
  1324.     /*
  1325.      * tell the user what is happening
  1326.      */
  1327.     error(TEMP, "Reading file '%s'...", name);
  1328.  
  1329.     /*
  1330.      * read the entire file, without going past end of buffer.
  1331.      * Note that this means a file that is within 1K of the limit
  1332.      *  will not be accepted.
  1333.      */
  1334.     limit -= 1024;
  1335.     for (;;) {
  1336.         if (start >= limit) {
  1337.             error(WARNING, "file '%s' too big", name);
  1338.             close(fd);
  1339.             return ERROR;
  1340.         }
  1341.         if ((length = read(fd, (char *)start, 1024)) == ERROR) {
  1342.             error(WARNING, "could not read file '%s'", name);
  1343.             close(fd);
  1344.             return ERROR;
  1345.         }
  1346.         start += length;
  1347.         if (length == 0) {
  1348.             /*
  1349.              * we reached the end of file
  1350.              */
  1351.             break;
  1352.         }
  1353.     }
  1354.  
  1355.     /*
  1356.      * close the file and report the final character in the buffer
  1357.      */
  1358.     close(fd);
  1359.     *end = start;
  1360.  
  1361.     return OK;
  1362. }
  1363.  
  1364. /*
  1365.  * Name:    write_file
  1366.  * Purpose: To write text to a file, eliminating trailing space on the
  1367.  *           way.
  1368.  * Date:    November 11, 1989
  1369.  * Passed:  name:  name of disk file or device
  1370.  *          mode:  fopen flags to be used in open
  1371.  *          start: first character in text buffer
  1372.  *          end:   last character (+1) in text buffer
  1373.  * Returns: OK, or ERROR if anything went wrong
  1374.  * Notes:   Trailing space at the very end of the text is NOT removed,
  1375.  *           so that a block write of a block of spaces will work.
  1376.  *          No error messages are displayed here, so the caller must
  1377.  *           both tell the user what is happening, and print an error
  1378.  *           message if anything goes wrong.
  1379.  *          This function is in the hardware dependent module because
  1380.  *           some computers require non-standard open parameters...
  1381.  */
  1382. static int write_file(name, mode, start, end)
  1383. char *name;
  1384. char *mode;
  1385. text_ptr start;
  1386. text_ptr end;
  1387. {
  1388.     FILE *fp;       /* file to be written */
  1389.     int spaces;     /* no. of space characters pending */
  1390.     char c;         /* current character in file */
  1391.  
  1392.     /*
  1393.      * create a new file, or truncate an old one
  1394.      */
  1395.     if ((fp = fopen(name, mode)) == NULL) {
  1396.         return ERROR;
  1397.     }
  1398.  
  1399.     /*
  1400.      * save the file, eliminating trailing space
  1401.      */
  1402.     spaces = 0;
  1403.     for (;;) {
  1404.         if (start == end) {
  1405.             break;
  1406.         }
  1407.         if ((c = *start++) == ' ') {
  1408.             spaces++;   /* count them, maybe output later */
  1409.             continue;
  1410.         }
  1411.  
  1412.         if (c == '\n') {
  1413.             spaces = 0; /* eliminate the trailing space */
  1414.         }
  1415.         else if (spaces) {
  1416.             /*
  1417.              * the spaces were NOT trailing, so output them now
  1418.              */
  1419.             do {
  1420.                 if (putc(' ', fp) == ERROR) {
  1421.                     fclose(fp);
  1422.                     return ERROR;
  1423.                 }
  1424.             } while (--spaces);
  1425.         }
  1426.  
  1427.         if (putc(c, fp) == ERROR) {
  1428.             fclose(fp);
  1429.             return ERROR;
  1430.         }
  1431.     }
  1432.  
  1433.     /*
  1434.      * output any trailing space at end of file - this may be important
  1435.      *  for block writes.
  1436.      */
  1437.     if (spaces) {
  1438.         do {
  1439.             if (putc(' ', fp) == ERROR) {
  1440.                 fclose(fp);
  1441.                 return ERROR;
  1442.             }
  1443.         } while (--spaces);
  1444.     }
  1445.  
  1446.     return fclose(fp);
  1447. }
  1448.  
  1449. /*
  1450.  * Name:    hw_save
  1451.  * Purpose: To save text to a file, eliminating trailing space on the
  1452.  *           way.
  1453.  * Date:    November 11, 1989
  1454.  * Passed:  name:  name of disk file
  1455.  *          start: first character in text buffer
  1456.  *          end:   last character (+1) in text buffer
  1457.  * Returns: OK, or ERROR if anything went wrong
  1458.  * Notes:   Trailing space at the very end of the file is NOT removed,
  1459.  *           so that a block write of a block of spaces will work.
  1460.  *          No error messages are displayed here, so the caller must
  1461.  *           both tell the user what is happening, and print an error
  1462.  *           message if anything goes wrong.
  1463.  *          This function is in the hardware dependent module because
  1464.  *           some computers require non-standard open parameters...
  1465.  */
  1466. int hw_save(name, start, end)
  1467. char *name;
  1468. text_ptr start;
  1469. text_ptr end;
  1470. {
  1471.     return write_file(name, g_job_file ? "w Ds1 R80" : "w Ds1 V", start, end);
  1472. }
  1473.  
  1474. /*
  1475.  * Name:    hw_append
  1476.  * Purpose: To append text to a file.
  1477.  * Date:    November 11, 1989
  1478.  * Passed:  name:  name of disk file
  1479.  *          start: first character in text buffer
  1480.  *          end:   last character (+1) in text buffer
  1481.  * Returns: OK, or ERROR if anything went wrong
  1482.  * Notes:   No error messages are displayed here, so the caller must
  1483.  *           both tell the user what is happening, and print an error
  1484.  *           message if anything goes wrong.
  1485.  *          This function is in the hardware dependent module because
  1486.  *           some computers require non-standard open parameters...
  1487.  */
  1488. int hw_append(name, start, end)
  1489. char *name;
  1490. text_ptr start;
  1491. text_ptr end;
  1492. {
  1493.     return write_file(name, "a Ds1", start, end);
  1494. }
  1495.  
  1496. /*
  1497.  * Name:    hw_print
  1498.  * Purpose: To print text to a printer.
  1499.  * Date:    November 11, 1989
  1500.  * Passed:  start: first character in text buffer
  1501.  *          end:   last character (+1) in text buffer
  1502.  * Returns: OK, or ERROR if anything went wrong
  1503.  * Notes:   This function is in the hardware dependent module because
  1504.  *           some computers require non-standard open parameters...
  1505.  */
  1506. int hw_print(start, end)
  1507. text_ptr start;
  1508. text_ptr end;
  1509. {
  1510.     char command[300];      /* printer device command */
  1511.     char *device;           /* printer device name */
  1512.     unsigned short status;  /* printer file equation status */
  1513.     unsigned short param;   /* more info about any error */
  1514.  
  1515.     /*
  1516.      * work out where to print
  1517.      */
  1518.     for (;;) {
  1519.         strcpy(command, "2");
  1520.         if (get_name("Printer 1N208 (1), 2S125 (2), Computer Center (3): ",
  1521.                 1, command) != OK) {
  1522.             return ERROR;
  1523.         }
  1524.         switch (atoi(command)) {
  1525.         case 1:
  1526.             device = "300";
  1527.             break;
  1528.         case 2:
  1529.             device = "309";
  1530.             break;
  1531.         case 3:
  1532.             device = "6";
  1533.             break;
  1534.         default:
  1535.             continue;
  1536.         }
  1537.         break;
  1538.     }
  1539.     sprintf(command, "FILE LP;DEV=%s\r", device);
  1540.     MPE_COMMAND(command, &status, ¶m);
  1541.     if (status) {
  1542.         error(WARNING, "File equation error %d/%d", status, param);
  1543.         return ERROR;
  1544.     }
  1545.  
  1546.     /*
  1547.      * print file
  1548.      */
  1549.     return write_file("LP", "a Ds1", start, end);
  1550. }
  1551.  
  1552. /*
  1553.  * Name:    hw_copy_path
  1554.  * Purpose: To create a new file path using most of an old path but
  1555.  *           changing just the file name.
  1556.  * Date:    November 8, 1989
  1557.  * Passed:  old:   the file path to extract path info from
  1558.  *          name:  the file name to add to the extracted path info
  1559.  * Returns: new:   the new path
  1560.  * Notes:   The file is located in the same place as the original, so
  1561.  *           that related editor files stay in the same directory.
  1562.  *          This function is hardware dependent because different characters
  1563.  *           delimit directories on different systems.
  1564.  */
  1565. void hw_copy_path(old, name, new)
  1566. char *old;
  1567. char *name;
  1568. char *new;
  1569. {
  1570.     char *cp;           /* cutoff point in old path */
  1571.  
  1572.     strcpy(new, name);
  1573.     if ((cp = strchr(old, '.')) != NULL) {
  1574.         strcat(new, cp);
  1575.     }
  1576. }
  1577.  
  1578. /*
  1579.  * Name:    hw_os_shell
  1580.  * Purpose: To shell out of the editor into the operating system, in such a
  1581.  *           way that editing may be resumed later.
  1582.  * Date:    November 28, 1990
  1583.  * Returns: TRUE if screen may have been clobbered, FALSE if screen OK.
  1584.  */
  1585. int hw_os_shell()
  1586. {
  1587.     return FALSE;   /* not implemented - possible for unpriviledged users? */
  1588. }
  1589.